/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using DaveSexton.DocProject.Sandcastle.Configuration;

namespace DaveSexton.DocProject.Sandcastle.BuildComponents
{
	public sealed partial class SandcastleBuildComponentDialog : Form
	{
		#region Public Properties
		[Browsable(false), DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public SandcastleBuildComponent SelectedComponent
		{
			get
			{
				DataGridViewRow row = SelectedRow;

				if (row == null)
					return null;
				else
					return (SandcastleBuildComponent) row.DataBoundItem;
			}
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				if (!buildComponents.Contains(value))
					throw new ArgumentException(Resources.Errors.BuildComponentNotContained, "value");

				componentsDataGridView.Focus();
				componentsDataGridView.ClearSelection();
				componentsDataGridView.Rows[buildComponents.IndexOf(value)].Selected = true;
			}
		}

		[Browsable(false)]
		public SandcastleBuildComponentManager Manager
		{
			get
			{
				return manager;
			}
		}
		#endregion

		#region Private / Protected
		private DataGridViewRow SelectedRow
		{
			get
			{
				if (componentsDataGridView.SelectedRows.Count == 1)
				{
					DataGridViewRow row = componentsDataGridView.SelectedRows[0];

					if (row != null && row.Index > -1)
						return row;
				}

				return null;
			}
		}

		private SandcastleBuildComponentManager manager;
		private SandcastleBuildComponentCollection buildComponents;
		private readonly HelpTypes helpType;
		private readonly Presentation presentation;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="SandcastleBuildComponentDialog" /> class.
		/// </summary>
		internal SandcastleBuildComponentDialog(SandcastleBuildComponentManager manager)
			: this()
		{
			if (manager == null)
				throw new ArgumentNullException("manager");

			this.manager = manager;
			this.helpType = manager.HelpType;

			resetButton.Visible = !manager.IsNested;

			Text = string.Format(System.Globalization.CultureInfo.CurrentCulture, Text, manager.DisplayName);
			
			presentation = SandcastleBuildEngineProvider.Presentations[manager.Options.PresentationName];

			manager.EnsureLoaded();

			buildComponents = manager.Components;

			componentsDataGridView.AutoGenerateColumns = false;

			source.DataSource = buildComponents;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="SandcastleBuildComponentDialog" /> class.
		/// </summary>
		private SandcastleBuildComponentDialog()
		{
			InitializeComponent();
		}
		#endregion

		#region Methods
		public void ResetComponents()
		{
			if (MessageBox.Show(DocProjectEnvironment.MainWindow, string.Format(System.Globalization.CultureInfo.CurrentCulture, 
				Resources.Text.ConfirmResetSandcastleConfigurationFile, Environment.NewLine),
				Resources.Text.ConfirmReset, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2,
				DocProjectEnvironment.MessageBoxOptions) == DialogResult.Yes)
			{
				presentation.ImportConfigurationFile(manager.Options.Project, helpType);

				manager.Reload();
				manager.IsDirty = true;

				source.ResetBindings(false);
			}
		}

		private void UpdateForSelectedRow()
		{
			DataGridViewRow row = SelectedRow;

			deleteButton.Enabled = row != null;
			upButton.Enabled = row != null && row.Index > 0;
			downButton.Enabled = row != null && row.Index < buildComponents.Count - 1;
		}

		private void AddComponent()
		{
			SandcastleBuildComponent lastComponent = null;

			using (SandcastleBuildComponentListDialog dialog = new SandcastleBuildComponentListDialog())
			{
				if (dialog.ShowDialog(this) == DialogResult.OK)
				{
					ICollection<Type> types = dialog.SelectedComponentTypes;

					if (types.Count > 0)
					{
						foreach (Type type in types)
						{
							string assemblyPathOverride;
							if (type.Assembly == BuildComponentsLoader.Instance.Assembly)
								assemblyPathOverride = System.IO.Path.Combine(
									SandcastleBuildEngineProvider.Paths.ToolsUnexpanded, 
									BuildComponentsLoader.AssemblyFileName);
							else
								assemblyPathOverride = null;

							lastComponent = manager.AddComponent(type, assemblyPathOverride);
						}
					}
				}
			}

			if (lastComponent != null)
			{
				ShowDefaultEditor(lastComponent);

				componentsDataGridView.Rows[componentsDataGridView.Rows.Count - 1].Selected = true;
			}
		}
		
		private void MoveSelectedComponentUp()
		{
			SandcastleBuildComponent component = SelectedComponent;

			DataGridViewRow row = SelectedRow;
			int index = (row != null) ? row.Index : -1; 	// index must be retrieved before MoveUp is called.

			if (component != null)
				manager.MoveUp(component);

			if (index > -1)
				componentsDataGridView.Rows[index - 1].Selected = true;
		}

		private void MoveSelectedComponentDown()
		{
			SandcastleBuildComponent component = SelectedComponent;

			DataGridViewRow row = SelectedRow;
			int index = (row != null) ? row.Index : -1;		// index must be retrieved before MoveDown is called.

			if (component != null)
				manager.MoveDown(component);

			if (index > -1)
				componentsDataGridView.Rows[index + 1].Selected = true;
		}

		private void HandleCellClick(DataGridViewCellEventArgs e)
		{
			if (e.RowIndex > -1 && e.ColumnIndex == editColumn.DisplayIndex)
				EditComponent((SandcastleBuildComponent) componentsDataGridView.Rows[e.RowIndex].DataBoundItem);
		}

		private void DeleteComponent(SandcastleBuildComponent component)
		{
			if (component != null)
				manager.Remove(component);
		}

		private void EditComponent(SandcastleBuildComponent component)
		{
			System.Drawing.Design.UITypeEditor editor = component.Editor;

			if (editor == null || editor.GetType() == typeof(Editors.SandcastleBuildComponentBasicEditor))
				ShowDefaultEditor(component);
			else
			{
				EditorContext context = new EditorContext(manager);

				if (editor.GetEditStyle(context) == System.Drawing.Design.UITypeEditorEditStyle.Modal)
				{
					buildComponents.SuspendChangeTacking();

					try
					{
						context.OnComponentChanging();

						object result = editor.EditValue(context, manager, component);
						bool changed;

						if (result is bool)
							changed = (bool) result;
						else
							changed = (context.Changed) ? true : result != null;		// assumption

						if (changed)
							manager.NotifyChanged(component, false);
						else
							component.CancelChanges();
					}
					finally
					{
						buildComponents.ResumeChangeTracking();
					}
				}
			}
		}

		private void ShowDefaultEditor(SandcastleBuildComponent component)
		{
			if (!component.IsNew && !DocProjectEnvironment.RunningInVisualStudio)
				// Visual Studio can be opened to edit the config using the XML editor when running externally.  
				// Otherwise, the general editor dialog is shown (below) while running in VS since this form is 
				// shown modally.
				System.Diagnostics.Process.Start(component.ConfigurationFile);
			else
			{
				buildComponents.SuspendChangeTacking();
				bool changed = false;

				try
				{
					changed = Editors.SandcastleBuildComponentBasicEditor.ShowDialog(this, component) == DialogResult.OK;
				}
				finally
				{
					buildComponents.ResumeChangeTracking();
				}

				if (changed)
				{
					component.CommitChanges();
					manager.NotifyChanged(component, false);

					source.ResetBindings(false);
				}
			}
		}
		#endregion

		#region Event Handlers
		private void upButton_Click(object sender, EventArgs e)
		{
			MoveSelectedComponentUp();
		}

		private void downButton_Click(object sender, EventArgs e)
		{
			MoveSelectedComponentDown();
		}

		private void deleteButton_Click(object sender, EventArgs e)
		{
			DeleteComponent(SelectedComponent);
		}

		private void addButton_Click(object sender, EventArgs e)
		{
			AddComponent();
		}

		private void resetButton_Click(object sender, EventArgs e)
		{
			ResetComponents();
		}

		private void componentsDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
		{
			HandleCellClick(e);
		}

		private void componentsDataGridView_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
		{
			HandleCellClick(e);
		}

		private void componentsDataGridView_SelectionChanged(object sender, EventArgs e)
		{
			UpdateForSelectedRow();
		}

		private void componentsDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
		{
			DataGridViewCell cell = componentsDataGridView[e.ColumnIndex, e.RowIndex];

			if (e.ColumnIndex == editColumn.DisplayIndex)
				cell.ToolTipText = editColumn.ToolTipText;
			else if (e.ColumnIndex == descriptionColumn.DisplayIndex)
			{
				e.Value = ((string) e.Value ?? string.Empty).Replace(Environment.NewLine, "  ");
				e.FormattingApplied = true;
			}
		}

		private void componentsDataGridView_KeyUp(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Delete)
				DeleteComponent(SelectedComponent);
		}
		#endregion

		#region Nested
		private sealed class EditorContext : ITypeDescriptorContext
		{
			#region Public Properties
			public bool Changed
			{
				get
				{
					return changed;
				}
			}
			#endregion

			#region Private / Protected
			private readonly SandcastleBuildComponentManager manager;
			private bool changed;
			#endregion

			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="EditorContext" /> class.
			/// </summary>
			public EditorContext(SandcastleBuildComponentManager manager)
			{
				this.manager = manager;
			}
			#endregion

			#region Methods

			#endregion

			#region ITypeDescriptorContext Members
			public IContainer Container
			{
				get
				{
					return null;
				}
			}

			public object Instance
			{
				get
				{
					return manager;
				}
			}

			public void OnComponentChanged()
			{
				changed = true;
			}

			public bool OnComponentChanging()
			{
				manager.NotifyChanging();
				return true;
			}

			public PropertyDescriptor PropertyDescriptor
			{
				get
				{
					return null;
				}
			}
			#endregion

			#region IServiceProvider Members
			public object GetService(Type serviceType)
			{
				return manager.GetService(serviceType);
			}
			#endregion
		}
		#endregion
	}
}